home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2007 December / PCWKCD1207B.iso / Blogowanie poza sfera / Flock 0.9.1.3 stable / flock-0.9.1.3.en-US.win32.exe / flock / components / flockMyworldService.js < prev    next >
Text File  |  2007-10-12  |  19KB  |  603 lines

  1. // BEGIN FLOCK GPL
  2. // 
  3. // Copyright Flock Inc. 2005-2007
  4. // http://flock.com
  5. // 
  6. // This file may be used under the terms of of the
  7. // GNU General Public License Version 2 or later (the "GPL"),
  8. // http://www.gnu.org/licenses/gpl.html
  9. // 
  10. // Software distributed under the License is distributed on an "AS IS" basis,
  11. // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. // for the specific language governing rights and limitations under the
  13. // License.
  14. // 
  15. // END FLOCK GPL
  16. //
  17.  
  18. // TODO JVL: queue is now a stack but the syntax continues to use 'queue'.  Will fix later.
  19.  
  20. // XPConnect Helpers
  21. var Cc = Components.classes;
  22. var Ci = Components.interfaces;
  23. var Cr = Components.results;
  24.  
  25. // Constants 
  26. const MYWORLD_SHORTNAME = "myworld";
  27. const MYWORLD_FULLNAME = "Myworld";
  28. const MYWORLD_TITLE = "Myworld Service";
  29. const MYWORLD_FAVICON = "http://www.flock.com/favicon.ico";
  30. const MYWORLD_CID = Components.ID("{bb35349f-fb23-3089-4d3d-28462e1c98eb}");
  31. const MYWORLD_CONTRACTID = "@flock.com/myworld-service;1";
  32. const CATEGORY_COMPONENT_NAME = "Myworld JS Component";
  33.  
  34. const CACHESERVICE_CONTRACTID = "@mozilla.org/network/cache-service;1";
  35. const TIMER_CONTRACTID = "@mozilla.org/timer;1";
  36. const XMLHTTPREQUEST_CONTRACTID = "@mozilla.org/xmlextras/xmlhttprequest;1";
  37.  
  38. const MAX_FLICKR_FARMS = 10;
  39. const PROTOCOL = "http://";
  40. const FLICKR_FARM = "farm";
  41. const MAX_RETRIEVAL_ATTEMPTS = 3;
  42. const DEFAULT_INTERVAL = 500;  // in milliseconds
  43. const DEFAULT_TIMER_TYPE = Ci.nsITimer.TYPE_REPEATING_PRECISE;
  44.  
  45.  
  46.  
  47. // Component Utils (just used for the timer/scheduler)
  48. var gCompTK;
  49. function getCompTK() {
  50.   if (!gCompTK) {
  51.     var gCompTK = Cc["@flock.com/singleton;1"]
  52.       .getService(Ci.flockISingleton)
  53.       .getSingleton("chrome://browser/content/flock/services/common/load-compTK.js")
  54.       .wrappedJSObject;
  55.   }
  56.   return gCompTK;
  57. }
  58.  
  59.  
  60.  
  61. // ===========================================================
  62. // ========== BEGIN flockMyworldService Component ============
  63. // ===========================================================
  64.  
  65.  
  66. // BEGIN Constructor
  67. function myworldService()
  68. {
  69.   this._logger = Cc["@flock.com/logger;1"].createInstance(Ci.flockILogger);
  70.   this._logger.init("MyworldService");
  71.  
  72.   this._logger.debug("BEGIN Ctor");
  73.   this._queue = [];
  74.  
  75.   this._ctk = {
  76.     interfaces: [
  77.       "nsISupports",
  78.       "nsIClassInfo",
  79.       "nsISupportsCString",
  80.       "nsIObserver",
  81.       "flockIMyworldService"
  82.     ],
  83.     shortName: MYWORLD_SHORTNAME,
  84.     fullName: MYWORLD_FULLNAME,
  85.     description: MYWORLD_TITLE,
  86.     favicon: MYWORLD_FAVICON,
  87.     CID: MYWORLD_CID,
  88.     contractID: MYWORLD_CONTRACTID
  89.   };
  90.  
  91.   this.obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
  92.   this.obs.addObserver(this, "xpcom-shutdown", false);
  93.   this.obs.addObserver(this, "flock-data-ready", false);
  94.  
  95.   this._coop = Cc["@flock.com/singleton;1"]
  96.     .getService(Ci.flockISingleton)
  97.     .getSingleton("chrome://browser/content/flock/common/load-faves-coop.js")
  98.     .wrappedJSObject;
  99.  
  100.   var inst = this;  // this is necessary as you cannot use 'this' within the defined notify function
  101.   var callback = { notify : function(aTimer) { inst.processQueue(); } };
  102.   this._timer = Components.classes[TIMER_CONTRACTID].createInstance(Ci.nsITimer);
  103.   this._timer.initWithCallback(callback, DEFAULT_INTERVAL, DEFAULT_TIMER_TYPE);
  104.  
  105.   this._logger.info("Myworld service created");
  106.   this._logger.debug("END Ctor");
  107. }
  108. // END Constructor
  109.  
  110. myworldService.prototype.init =
  111. function myworldService_init()
  112. {
  113.   var loosePhotosEnum = this._coop.RichPhoto.all();
  114.   while (loosePhotosEnum.hasMoreElements()) {
  115.     var photo = loosePhotosEnum.getNext();
  116.     if (!photo.cachedThumbnail || photo.cachedThumbnail == "") {
  117.       this.queueResource(photo.id());
  118.     }
  119.   }
  120. }
  121.  
  122. // BEGIN nsIObserver
  123. myworldService.prototype.observe = function (subject, topic, data)
  124. {
  125.   if (topic == "xpcom-shutdown")
  126.   {
  127.     this.obs.removeObserver(this, "xpcom-shutdown");
  128.   } else if (topic == "flock-data-ready") {
  129.     this.obs.removeObserver(this, "flock-data-ready");
  130.     this.init();
  131.   }
  132. }
  133. // END nsIObserver
  134.  
  135.  
  136. // BEGIN flockIMyworldService
  137. myworldService.prototype.flags = Ci.nsIClassInfo.SINGLETON;
  138.  
  139. // queue a unique resource for processing
  140. myworldService.prototype.queueResource =
  141. function myworldService_queueResource(aURN)
  142. {
  143.   this._logger.debug("BEGIN queueResource: " + aURN);
  144.   if (!this.queueResourceExists(aURN))
  145.   {
  146.     this.createQueueResource(aURN);
  147.   }
  148.   this._logger.debug("END queueResource: " + aURN);
  149. }
  150.  
  151.  
  152. // clear the queue
  153. myworldService.prototype.clearQueue =
  154. function myworldService_clearQueue()
  155. {
  156.   while(this._queue.length > 0)
  157.   {
  158.     this._queue.shift();
  159.   }
  160.   this._logger.info("queue cleared");
  161. }
  162.  
  163.  
  164. // check the browser cache for the given key
  165. // returns a valid cached key or null if not found
  166. myworldService.prototype.checkCache = 
  167. function myworldService_checkCache(aKey)
  168. {
  169.   this._logger.debug("BEGIN checkCache: " + aKey);
  170.   if (aKey)
  171.   {
  172.     var cacheSession = this.getCacheSession();
  173.     if (cacheSession)
  174.     {
  175.       // check in the browser cache for the given key
  176.       if (this.isValidCacheEntry(cacheSession, aKey))
  177.       {
  178.         this._logger.debug("END checkCache: " + aKey);
  179.         return aKey;
  180.       }
  181.  
  182.       // we couldn't find the given resource in the browser cache, it may have been forwarded to a farm...
  183.       // trim the url(key) for our search needs and search for this modified url(key)
  184.       var keyBase = aKey;
  185.       if (aKey.indexOf(PROTOCOL) == 0)
  186.       {
  187.         keyBase = aKey.substr(PROTOCOL.length);
  188.       }
  189.  
  190.       for (var idx = 1; idx < MAX_FLICKR_FARMS + 1; idx++)
  191.       {
  192.         var key = PROTOCOL + FLICKR_FARM + idx + "." + keyBase;
  193.         if (this.isValidCacheEntry(cacheSession, key))
  194.         {
  195.           this._logger.debug("END checkCache: " + key);
  196.           return key;
  197.         }
  198.       }
  199.     }
  200.   }
  201.  
  202.   this._logger.debug("END checkCache: null");
  203.   return null;
  204. }
  205. // END flockIMyworldService
  206.  
  207.  
  208. // BEGIN flockMyworldService
  209. // returns the cache session or null if an error occured
  210. myworldService.prototype.getCacheSession =
  211. function myworldService_getCacheSession()
  212. {
  213.   var cacheService = Cc[CACHESERVICE_CONTRACTID].getService(Ci.nsICacheService);
  214.   if (!cacheService)
  215.   {
  216.     this._logger.debug("getCacheSession: No cache service");
  217.     return null;
  218.   }
  219.  
  220.   // check both memory and disk cache
  221.   // NOTE: 'HTTP' is the name for the browser cache
  222.   var cacheSession = cacheService.createSession("HTTP", Ci.nsICache.STORE_ANYWHERE, true);
  223.   if (!cacheSession)
  224.   {
  225.     this._logger.debug("getCacheSession: No cache session");
  226.     return null;
  227.   }
  228.  
  229.   cacheSession.doomEntriesIfExpired = false;
  230.   return cacheSession;
  231. }
  232.  
  233.  
  234. // check that the given key is in the browser cache
  235. // returns true if found, false otherwise
  236. myworldService.prototype.isValidCacheEntry =
  237. function isValidCacheEntry(cacheSession, aKey)
  238. {
  239.   this._logger.debug("BEGIN isValidCacheEntry: " + aKey);
  240.   if (!cacheSession)
  241.   {
  242.     this._logger.debug("END isValidCacheEntry: No cache session");
  243.     return false;
  244.   }
  245.  
  246.   if (!aKey || aKey.length < 1)
  247.   {
  248.     this._logger.debug("END isValidCacheEntry: No key supplied");
  249.     return false;
  250.   }
  251.  
  252.   try
  253.   {
  254.     // checks browser cache for given key(url)
  255.     // found if a cacheEntryDescriptor is returned and the resource's size is > 0,
  256.     // otherwise it will throw an exception
  257.     var cacheEntryDescriptor = cacheSession.openCacheEntry(aKey, Ci.nsICache.ACCESS_READ, false);
  258.     var found = this.isNonEmptyResource(cacheEntryDescriptor);
  259.     var msg = "END isValidCacheEntry: openCacheEntry: " + aKey + (found ? " (found in cache)" : " (not found in cache)");
  260.     this._logger.debug(msg);
  261.     return found;
  262.   }
  263.   catch (ex)
  264.   {
  265.     this._logger.debug("isValidCacheEntry: " + aKey + " (not in cache)");
  266.   }
  267.  
  268.   this._logger.debug("END isValidCacheEntry: false");
  269.   return false;
  270. }
  271.  
  272.  
  273. // helper function to determine if the cacheEntryDescriptor is:
  274. // 1) not null
  275. // 2) size > 0
  276. // also reports additional details if showDetails = true
  277. // returns true if it passes the above criteria, false otherwise
  278. myworldService.prototype.isNonEmptyResource =
  279. function isNonEmptyResource(cacheEntryDescriptor, showDetails)
  280. {
  281.   if (showDetails)
  282.     this._logger.debug("isNonEmptyResource:");
  283.  
  284.   // NOTE: when checking cacheEntryDescriptor for nullness, we need to check it explictly against null.  '!cacheEntryDescriptor' will not work!
  285.   var found = (cacheEntryDescriptor != null);
  286.  
  287.   if (showDetails)
  288.     this._logger.debug("    descriptor: " + (found ? "not null" : "null"));
  289.  
  290.   var size = cacheEntryDescriptor.dataSize;
  291.  
  292.   if (showDetails)
  293.     this._logger.debug("    size: " + size);
  294.  
  295.   return found && (size > 0);
  296. }
  297.  
  298.  
  299. // process the elements stored in the queue
  300. myworldService.prototype.processQueue =
  301. function processQueue()
  302. {
  303.   var elem = null;
  304.   var coopObj = null;
  305.   var thumbnail = null;
  306.  
  307.   while (1)
  308.   {
  309.     //this._logger.debug("BEGIN processQueue: current queue size: " + this._queue.length);
  310.     if (this._queue.length < 1)
  311.     {
  312.       //this._logger.debug("END processQueue: nothing in the queue to process");
  313.       return;
  314.     }
  315.  
  316.     // pop the first entry off the queue for processing
  317.     elem = this._queue.shift();
  318.     if (!elem)
  319.     {
  320.       this._logger.debug("END processQueue: element popped off queue is null, nothing to process");
  321.       return;
  322.     }
  323.  
  324.     this._logger.debug("processQueue:");
  325.     this._logger.debug("    processing: " + elem.urn);
  326.     this._logger.debug("    items left in queue: " + this._queue.length);
  327.     
  328.     // retrieve the appropriate resource from the rdf in memory
  329.     coopObj = this._coop.get(elem.urn);
  330.     if (!coopObj)
  331.     {
  332.       this._logger.debug("END processQueue: " + elem.urn + " (not in rdf)");
  333.       return;
  334.     }
  335.  
  336.     thumbnail = coopObj.thumbnail;
  337.     this._logger.debug("processQueue: thumbnail: " + thumbnail);
  338.  
  339.     // if the retrieved resource doesn't have topmedia as a parent,
  340.     // discard from the queue and just set the regular thumbnail in hopes it can show something
  341.     if (this.belongsToTopMedia(coopObj))
  342.     {
  343.       this._logger.debug("processQueue: " + elem.urn + " (found in topmedia)");
  344.       break;
  345.     }
  346.     else
  347.     {
  348.       this._logger.debug("processQueue: " + elem.urn + " (not in topmedia, process the next item in the queue)");
  349.       coopObj.cachedThumbnail = thumbnail;
  350.     }
  351.   }
  352.  
  353.   // we only need to update the rdf with the cachedThumbnail if it doesn't already exist
  354.   if (!this.isValidCachedThumbnail(coopObj.cachedThumbnail, thumbnail))
  355.   {
  356.     // take a look in the browser cache for the cached thumbnail url
  357.     thumbnail = this.checkCache(thumbnail);
  358.     if (thumbnail)
  359.     {
  360.       this._logger.debug("processQueue: " + thumbnail + " (in cache)");
  361.       
  362.       // found the cached thumbnail url, now store it in the rdf; we're done!
  363.       coopObj.cachedThumbnail = thumbnail;
  364.     }
  365.     else
  366.     {
  367.       this._logger.debug("processQueue: " + coopObj.thumbnail + " (not in cache)");
  368.       coopObj.cachedThumbnail = "";
  369.       
  370.       // we couldn't find the cached thumbnail url in the browser cache so retrieve the resource
  371.       // from the social service to see if we can cache it ourselves and retrieve it later
  372.       this.reRequestResource(coopObj.thumbnail);
  373.  
  374.       this._logger.debug("processQueue: re-add to queue:");
  375.       this._logger.debug("    URN: " + elem.urn);
  376.       this._logger.debug("    current attempt: " + elem.attempt);
  377.  
  378.       // since it wasn't in the browser cache this time, re-add the queued entry to the end of the
  379.       // queue; we will try retrieving it later (for a maximum of MAX_RETRIEVAL_ATTEMPTS) otherwise,
  380.       // just fail as there might be something wrong with the social service itself
  381.       this.requeueResource(elem, coopObj)
  382.     }
  383.   }
  384.  
  385.   this._logger.debug("END processQueue");
  386. }
  387.  
  388.  
  389. // checks to see if the coop object has a topmedia as a parent
  390. // returns true if topmedia is a parent, false otherwise
  391. myworldService.prototype.belongsToTopMedia =
  392. function myworldService_belongsToTopMedia(coopObj)
  393. {
  394.   if (!coopObj)
  395.   {
  396.     return false;
  397.   }
  398.  
  399.   var parents = coopObj.getParents();
  400.   if (!parents)
  401.   {
  402.     return false;
  403.   }
  404.  
  405.   for (var idx in parents)
  406.   {
  407.     var parent = parents[idx];
  408.     if (parent)
  409.     {
  410.       var grandParents = parent.getParents();
  411.       if (grandParents)
  412.       {
  413.         for (var idxGP in grandParents)
  414.         {
  415.           var grandParent = grandParents[idxGP];
  416.           if (grandParent.id() == "urn:flock:topmedia")
  417.           {
  418.             this._logger.debug("belongsToTopMedia: belongs to topmedia!");
  419.             return true;
  420.           }
  421.         }
  422.       }
  423.     }
  424.   }
  425.  
  426.   return false;
  427. }
  428.  
  429.  
  430. // check if the cachedThumbnail is:
  431. // 1) not null or empty and
  432. // 2) not the loading picture
  433. // returns true if the cachedThumbnail passes the above criteria, false otherwise
  434. myworldService.prototype.isValidCachedThumbnail =
  435. function myworldService_isValidCachedThumbnail(cachedThumbnail, thumbnail)
  436. {
  437.   return cachedThumbnail && cachedThumbnail.length > 0;
  438. }
  439.  
  440.  
  441. // re-retrieve the resource from the social service to see if we can cache it ourselves and retrieve it later
  442. myworldService.prototype.reRequestResource =
  443. function myworldService_reRequestResource(thumbnail)
  444. {
  445.   this._logger.debug("BEGIN reRequestResource");
  446.   if (thumbnail)
  447.   {
  448.     var request = Cc[XMLHTTPREQUEST_CONTRACTID].createInstance(Ci.nsIXMLHttpRequest);
  449.     if (!request)
  450.     {
  451.       this._logger.debug("END reRequestResource: unable to instantiate HTTPRequest");
  452.       return;
  453.     }
  454.  
  455.     try
  456.     {
  457.       this._logger.debug("reRequestResource: HTTPRequest sent for " + thumbnail);
  458.       request.open("GET", coopObj.thumbnail, true);
  459.       request.send(null);
  460.     }
  461.     catch (ex)
  462.     {
  463.       this._logger.debug("reRequestResource: HTTPRequest failed for " + thumbnail);
  464.     }
  465.   }
  466.   this._logger.debug("END reRequestResource");
  467. }
  468.  
  469.  
  470. // requeue the resource unless the maximum attempts to retrieve the resource has been exceeded
  471. myworldService.prototype.requeueResource =
  472. function myworldService_requeueResource(elem, coopObj)
  473. {
  474.   this._logger.debug("BEGIN requeueResource");
  475.   elem.attempt++;
  476.   if (elem.attempt < MAX_RETRIEVAL_ATTEMPTS)
  477.   {
  478.     this._queue.unshift(elem);
  479.     this._logger.debug("requeueResource: " + elem.urn + " (re-added to queue successfully)");
  480.   }
  481.   else
  482.   {
  483.     // number of attempts exceeded, just set the displayed thumbnail to the original resource
  484.     coopObj.cachedThumbnail = coopObj.thumbnail;
  485.     this._logger.debug("requeueResource: " + elem.urn + " (not re-added to queue as we've already tried " + MAX_RETRIEVAL_ATTEMPTS + " times)");
  486.   }
  487.  
  488.   this._logger.debug("    number of items in queue: " + this._queue.length);
  489.   this._logger.debug("END requeueResource");
  490. }
  491.  
  492.  
  493. // check if the resource already exists in the queue to be processed
  494. // returns false if it is unique to the queue, true otherwise
  495. myworldService.prototype.queueResourceExists =
  496. function myworldService_queueResourceExists(aURN)
  497. {
  498.   this._logger.debug("BEGIN queueResourceExists: " + aURN);
  499.   if (!aURN || aURN.length < 1)
  500.   {
  501.     this._logger.debug("END queueResourceExists: true (empty string)");
  502.     return true;
  503.   }
  504.  
  505.   // ensure we don't add duplicates to the queue
  506.   // NOTE: This may become an issue if there are an excessive number of items in the queue
  507.   for (var idx = 0; idx < this._queue.length; idx++)
  508.   {
  509.     if (this._queue[idx].urn == aURN)
  510.     {
  511.       this._logger.debug("END queueResourceExists: true (already exists)");
  512.       return true;
  513.     }
  514.   }
  515.  
  516.   this._logger.debug("END queueResourceExists: false");
  517.   return false;
  518. }
  519.  
  520.  
  521. // create a new resource and add it to the service queue to be processed
  522. myworldService.prototype.createQueueResource =
  523. function myworldService_createQueueResource(aURN)
  524. {
  525.   if (aURN && aURN.length > 0)
  526.   {
  527.     // add a new url/retrieval attempts (key/value) pair to the queue
  528.     var obj = {};
  529.     obj.urn = aURN;
  530.     obj.attempt = 0;
  531.     this._logger.debug("createQueueResource:");
  532.     this._logger.debug("    URN: " + obj.urn);
  533.     this._logger.debug("    attempt: " + obj.attempt);
  534.     this._queue.unshift(obj);
  535.   }
  536. }
  537. // END flockMyworldService
  538.  
  539.  
  540. // ========== END flockMyworldService Component ============
  541.  
  542.  
  543.  
  544. // ================================================
  545. // ========== BEGIN XPCOM Module support ==========
  546. // ================================================
  547.  
  548. const ENABLE_DEBUG = false; // switch to turn off slow debug code for production
  549. function DEBUG(x) { if (ENABLE_DEBUG) debug("flockMyworldService: "+x+"\n"); }
  550.  
  551. function createModule(aParams) {
  552.   DEBUG("createModule called\n    CID: " + aParams.CID + "\n    componentName: " + aParams.componentName + "\n    contractID: " + aParams.contractID + "\n    componentClass: " + aParams.componentClass);
  553.   return {
  554.     registerSelf: function (aCompMgr, aFileSpec, aLocation, aType) {
  555.       aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
  556.       aCompMgr.registerFactoryLocation( aParams.CID, aParams.componentName,
  557.                                         aParams.contractID, aFileSpec,
  558.                                         aLocation, aType );
  559.       var catMgr = Cc["@mozilla.org/categorymanager;1"]
  560.         .getService(Ci.nsICategoryManager);
  561.       catMgr.addCategoryEntry( "flock-startup", aParams.componentName,
  562.                                "service,"+aParams.contractID, true, true ); 
  563.       if (!aParams.categories) { aParams.categories = []; }
  564.       for (var i = 0; i < aParams.categories.length; i++) {
  565.         var cat = aParams.categories[i];
  566.         catMgr.addCategoryEntry( cat.category, cat.entry,
  567.                                  cat.value, true, true );
  568.       }
  569.     },
  570.     getClassObject: function (aCompMgr, aCID, aIID) {
  571.       if (!aCID.equals(aParams.CID)) { throw Cr.NS_ERROR_NO_INTERFACE; }
  572.       if (!aIID.equals(Ci.nsIFactory)) { throw Cr.NS_ERROR_NOT_IMPLEMENTED; }
  573.       return { // Factory
  574.         createInstance: function (aOuter, aIID) {
  575.           if (aOuter != null) { throw Cr.NS_ERROR_NO_AGGREGATION; }
  576.           var comp = new aParams.componentClass();
  577.           if (aParams.implementationFunc) { aParams.implementationFunc(comp); }
  578.           rv = comp.QueryInterface(aIID);
  579.           return rv;
  580.         }
  581.       };
  582.     },
  583.     canUnload: function (aCompMgr) { return true; }
  584.   };
  585. }
  586.  
  587. // NS Module entrypoint
  588. function NSGetModule(aCompMgr, aFileSpec) {
  589.   DEBUG("NSGetModule called");
  590.   return createModule({
  591.     componentClass: myworldService,
  592.     CID: MYWORLD_CID,
  593.     contractID: MYWORLD_CONTRACTID,
  594.     componentName: CATEGORY_COMPONENT_NAME,
  595.     implementationFunc: function (aComp) { getCompTK().addAllInterfaces(aComp); },
  596.     categories: [
  597.     ]
  598.   });
  599. }
  600.  
  601.  
  602. // ========== END XPCOM Module support ==========
  603.